package handwritten.gesture;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.io.Serializable;
import java.util.ArrayList;

import javax.xml.bind.annotation.XmlType;

import utility.math.Transformation;
import utility.math.Vector2D;

/**
 * @author tioz
 *
 */
/**
 * @author tioz
 *
 */
/**
 * @author tioz
 *
 */



@XmlType (
	    name = "##default", 
	    propOrder = {""}, 
	    namespace = "##default" 
	    //factoryClass = DEFAULT.class, 
	    //factoryMethod = ""
	)


public class Gesture implements Serializable,Cloneable{


	/**
	 * 
	 */
	private static final long serialVersionUID = 1990035651751853748L;
	// Path of the mous
	ArrayList<Vector2D> path;
	
	//put the path in a horizonthal rectangle
	
	
	/**
	 * initilise the ArrayList of position
	 */
	public Gesture(){
		path   = new ArrayList<Vector2D>();
	}
	public Gesture(ArrayList<Vector2D> path){
		this.path = new ArrayList<Vector2D>(path);
	}
	
	void offsetPath(Vector2D offset)
	{
		for(Vector2D coord: path)
		{
			coord.thisMinus(offset);
		}
	}
	
	/**
	 * Initialise the ArrayList with the initial position
	 * @param initialePosittion 
	 */
	public Gesture(Vector2D initialePosittion ) {
		this();
		path.add(initialePosittion);
	}

	/**
	 * @param g
	 */

		
	public void appendToPath(Vector2D newPos)
	{
		path.add(newPos);
		
	}
	/**
	 * @return the end of the path
	 */
	public Vector2D startOfPath(){
		if(path.size()!=0)
			return path.get(0);
		else
			return null;
		
	}
	
	/**
	 * @return The start of the path
	 */
	public Vector2D endOfPath(){
		if(path.size()!=0)
			return path.get(path.size()-1);
		else
			return null;
	}
	
	public ArrayList<Vector2D> getPath()
	{
		return path;
	}

	
	public void  fillPath(int[][] cell){
		//Bresenham's line algorithm WIKIPEDIA
		
		for(int i= 0;i < path.size()-2;i++ )
		{
			fillLine(cell,path.get(i),path.get(i+1),false);
		}
		if(path.size() >=2)
			fillLine(cell,path.get(path.size()-2),path.get(path.size()-1),true);
		
	}
	
	public void fillPath(int[][] cell,Vector2D offset){
		for(int i= 0;i < path.size()-2;i++ )
		{
			fillLine(cell,path.get(i).minus(offset),path.get(i+1).minus(offset),false);
		}
		if(path.size() >=2)
			fillLine(cell,path.get(path.size()-2).minus(offset),path.get(path.size()-1).minus(offset),true);
	}
	
		
	/**
	 * interpolate lineray the path point and increment by 1 the value of cell where the path is.
	 * the point out of the cell are not taken into acount
	 * @param cell
	 * @param start start of the path with a position relative to the cell
	 * @param end
	 * @param drawEnd
	 */
	static void fillLine(int[][] cell, Vector2D start,Vector2D end,boolean drawEnd){
	
		int x0= (int)start.getX();
		int y0= (int)start.getY();
		int x1= (int)end.  getX();
		int y1= (int)end.  getY();
		
		//System.out.println(x0+"  " +y0 );
		//System.out.println(x1+"  " +y1 );
		//System.out.println("loop" );
		
		int slopX=  (x0 < x1) ? 1 : -1;
		int slopY=  (y0 < y1) ? 1 : -1;
		
		int dx = Math.abs(x1-x0);
		int dy = Math.abs(y1-y0);
		
		int err = dx-dy;
		
		/*System.out.println("new loop pose");
		System.out.println(x0+"  " +y0 );
		System.out.println(x1+"  " +y1 );
		System.out.println("start");*/
		

		int xOld=x0;
		int yOld=y0;
		
		while(x0 != x1 || y0 != y1)
		{

			int e2 = 2*err;
			
			if(e2 >-dy)
			{
				err = err-dy;
				xOld=x0;
				x0= x0+slopX;
			}
					
			if(e2 < dx)
			{
				err = err+dx;
				yOld=y0;
				y0= y0+slopY;
			}
						
			if(0 < xOld && xOld< cell.length && 0< yOld && yOld < cell[0].length && (xOld!=x0 || yOld != y0))
			{
				cell[xOld][yOld]+=1;
				//System.out.println("in bound for "+ x0+"/"+x1 + " and "+y0+"/"+y1);
				//System.out.println(x0+"  " +y0 );
				//System.out.println(x1+"  " +y1 );
			}
		}
		
		if(drawEnd && 0 < x0 && x0< cell.length && 0< y0 && y0< cell[0].length && (xOld!=x0 || yOld != y0))
		{
			cell[x0][y0]+=1;
			//System.out.println(x0+"  " +y0 );
		}
	}
		
	/**
	 * TODO maybe computing the one with the linked point
	 * @return the center of mass of the inputed gesture.
	 */
	public Vector2D centerOfMass(){
		
		Vector2D centerOfM = new Vector2D(0,0);
		
		for(Vector2D points: path)
		{
			centerOfM = centerOfM.plus(points);
		}
		centerOfM.thisTime(1./path.size());
		
		return centerOfM;
	}
	
	/**
	 * @return the number of point in the path
	 */
	public int nPoint()
	{
		return path.size();
	}
	
	
	/**
	 * @return the max y coordinate
	 */
	public Vector2D  top(){
		
		Vector2D top = path.get(0);
		for(Vector2D p : path)
		{
			if(top.getY()< p.getY())
			top= p;
		}
		return new Vector2D(top);
	}
	
	
	/**
	 * @return return 		public Vector2D get(int i){
			return path.get(i);
		}the min y corrdinate
	 */
	public Vector2D bottom(){
		Vector2D bottom = path.get(0);
		for(Vector2D p : path)
		{
			if(bottom.getY()> p.getY())
			bottom= p;
		}
		return new Vector2D(bottom);
	}
	
	/**
	 * @return the max y coordinate
	 */
	public Vector2D  right(){
		
		Vector2D right = path.get(0);
		for(Vector2D p : path)
		{
			if(right.getX()< p.getX())
				right= p;
		}
		return new Vector2D(right);
	}
	
	
	/**
	 * @return return the min x coordinate
	 */
	public Vector2D left(){
		Vector2D left = path.get(0);
		for(Vector2D p : path)
		{
			if(left.getX() > p.getX())
				left= p;
						
		}
		return new Vector2D(left);
	}
	
	
	/**
	 * @return the height of the gesture
	 */
	public double Height(){
		return (top().minus(bottom())).getY();
	}
	
	/**
	 * @return the width of the gesture
	 */
	public double Width(){
		return right().minus(left()).getX();
	}
	
	
	public ArrayList<Double> pathDirection()
	{
		ArrayList<Double> dir = new ArrayList<Double>();
		Vector2D direction;
		
		for(int i=0;i<path.size()-1;i++)
		{
			direction= path.get(i+1).minus(path.get(i));
			dir.add(Math.atan2(  direction.getY() ,  direction.getX() ));
		}
		return dir;
	}
	
	public double pathLength(){
		
		double length=0;
		
		for(int i = 0; i< path.size()-1;i++)
		{
			length+=(path.get(i).minus(path.get(i+1))).norm();
		}	
		return length;
	}
	
	
	
	
	/**
	 * @param g2d
	 */
	public void paint(Graphics2D g2) {
		g2.setStroke(new BasicStroke(1));
		g2.setColor(new Color(100,50,0));
		
		for(int i = 0; i< path.size()-1;i++)
		{
			 //Line2D.Float point = new Line2D.Float(path.get(i).getX(),path.get(i).getY(),path.get(i+1).getX(),path.get(i+1).getY());    	 
			 Line2D.Double point = new Line2D.Double(path.get(i).getX(),path.get(i).getY(),path.get(i).getX(),path.get(i).getY()); 
			 g2.draw(point);
			 g2.fill(point);  // solid
		}	
	}
	
	
	public ArrayList<Vector2D> intersection()
	{
		//http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
		//+ use comment must check p annd h to know if intersect
		
		ArrayList<Vector2D> intersect = new ArrayList<Vector2D>();
		
		for(int i=0;i<path.size()-1;i++)
		{//+2 because study segment from i to i+1 
			Vector2D ligne1= path.get(i+1).minus(path.get(i));
			//perpendiculaire to the ligne1
			Vector2D perp1 = new Vector2D(-ligne1.getY(),ligne1.getX());
			
			for(int j=i+1;j<path.size()-1;j++)
			{
				Vector2D ligne2= path.get(j+1).minus(path.get(j));
				Vector2D perp2 = new Vector2D(-ligne2.getY(),ligne2.getX());
				
				double dividh = ligne2.dotProduct(perp1);
				double dividp = ligne1.dotProduct(perp2);
				
				if(dividh !=0 || dividp!=0)
				{
					double h = (path.get(i).minus(path.get(j))).dotProduct(perp1) /dividh;
					double p = (path.get(j).minus(path.get(i))).dotProduct(perp2) /dividp;
										
					// h== 0 is the start of the new segment
					if(h>0 && h<=1&& p>0 && p<=1)
					{
						intersect.add(path.get(j).plus(ligne2.time(h)));
					}
				}
			}
		}
		
		return intersect;
	}
	
	
	
	public ArrayList<Vector2D> intersection(Gesture gest)
	{
		//http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
		//+ use comment must check p annd h to know if intersect
		
		ArrayList<Vector2D> intersect = new ArrayList<Vector2D>();
		
		for(int i=0;i<path.size()-1;i++)
		{//+2 because study segment from i to i+1 
			Vector2D ligne1= path.get(i+1).minus(path.get(i));
			//perpendiculaire to the ligne1
			Vector2D perp1 = new Vector2D(-ligne1.getY(),ligne1.getX());
			
			for(int j=0;j<gest.path.size()-1;j++)
			{
				Vector2D ligne2= gest.path.get(j+1).minus(gest.path.get(j));
				Vector2D perp2 = new Vector2D(-ligne2.getY(),ligne2.getX());
				
				double dividh = ligne2.dotProduct(perp1);
				double dividp = ligne1.dotProduct(perp2);
				
				 
				if(dividh !=0 || dividp!=0)
				{
					double h = (path.get(i).minus(gest.path.get(j))).dotProduct(perp1) /dividh;
					double p = (gest.path.get(j).minus(path.get(i))).dotProduct(perp2) /dividp;
										
					//thism time we include 0 mais pas 1 car si h==1 le suivant ==0 mais pas focement l'inverse
					if(h>0 && h<=1&& p>0 && p<=1)
					{
						intersect.add(gest.path.get(j).plus(ligne2.time(h)));
					}
				}
			}
		}
		
		return intersect;
	}
	
	public Object clone() {
		Gesture gest= null;
		try
		{
			gest = (Gesture) super.clone();
			
		}
		catch(CloneNotSupportedException e)
		{
			e.printStackTrace(System.err);
		}	
		
		ArrayList<Vector2D> deepClone = new ArrayList<Vector2D>();
		for(Vector2D v : path)
		{
			deepClone.add((Vector2D) v.clone());
		}
		gest.path = deepClone;		
		
		return gest;
	}
	/**
	 * @param path the path to set
	 */
	public void setPath(ArrayList<Vector2D> path) {
		this.path = path;
	}
	
	public Vector2D get(int i){
		return path.get(i);
	}
	/**
	 * @param v
	 */
	public void translate(Vector2D d) {
		for(Vector2D v:path)
		{
			v.thisPlus(d);
		}
		
	}
	
	public int size(){
		return path.size();
	}
	
	
	public void transform(Transformation t) {
		for(Vector2D v:path)
		{
			t.transform(v);
		}
		
	}
	
	
	
}
	